// ImageClustering.cpp : implementation file
//

#include "stdafx.h"
#include "ImageLab.h"
#include "ImageClustering.h"
#include "atlimage.h"
#include "ChildFrm.h"
#include "IO.h"
#include "MultiscaleNet.h"
#define MAXITEM 21
#include "ImageAnnotation.h"
#include "SOM.h"
#include "matrix.h"
#include <locale>

// CImageClustering dialog

IMPLEMENT_DYNAMIC(CImageClustering, CDialog)

CImageClustering::CImageClustering(CWnd* pParent /*=NULL*/)
	: CDialog(CImageClustering::IDD, pParent)
{
	ASSERT(pParent!=NULL);
	parent = pParent;
	dialogid = CImageClustering::IDD;
	::AfxInitRichEdit2();
}

CImageClustering::~CImageClustering()
{
}

void CImageClustering::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

//Create the dialog
BOOL CImageClustering::Create()
{
	BOOL dialogcreate = CDialog::Create(dialogid, parent);
	//If failed to create the dialog
	if(dialogcreate == FALSE)
		return FALSE;

	//Set scrollbar information
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	scrollbar->SetScrollRange(0, 0, 0);
	SCROLLINFO scrollinfo;
	scrollbar->GetScrollInfo(&scrollinfo);
	scrollinfo.nPage = 10;
	scrollbar->SetScrollInfo(&scrollinfo, 1);
	scrollbar->SetScrollPos(0, 1);
	int currenttop = 0;
	int currentselection = -1;

	return TRUE;
}

//
BEGIN_MESSAGE_MAP(CImageClustering, CDialog)
	ON_BN_CLICKED(IDC_ADDIMAGES, &CImageClustering::OnBnClickedAddimage)
	ON_WM_VSCROLL()
	ON_LBN_SELCHANGE(IDC_IMAGECLUSTERING_IMAGELIST, &CImageClustering::OnLbnSelchangeImageclusteringImagelist)
	ON_LBN_SELCHANGE(IDC_IMAGECLUSTERING_CLUSTERLIST, &CImageClustering::OnLbnSelchangeImageclusteringClusterlist)
	ON_BN_CLICKED(IDC_ADDFOLDER, &CImageClustering::OnBnClickedAddfolder)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING, &CImageClustering::OnBnClickedImageclustering)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING_SORBYIMAGE, &CImageClustering::OnBnClickedImageclusteringSorbyimage)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING_SORBYCLUSTER, &CImageClustering::OnBnClickedImageclusteringSorbycluster)
	ON_BN_CLICKED(IDC_REMOVEFALL, &CImageClustering::ClusteringExperiment)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING_SAVERESULTS, &CImageClustering::OnBnClickedImageclusteringSaveresults)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING_LOADRESULTS, &CImageClustering::OnBnClickedImageclusteringLoadresults)
	ON_BN_CLICKED(IDC_IMAGECLUSTERING_LOADDB, &CImageClustering::OnBnClickedImageclusteringLoaddb)
END_MESSAGE_MAP()


// CImageClustering message handlers

//Add an image to the clustering
void CImageClustering::OnBnClickedAddimage()
{
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	//Get the supported image format
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT hResult;

	hResult = CImage::GetExporterFilterString(strFilter,aguidFileTypes);
	if (FAILED(hResult)) {
		return;
	}

	//Pop up a dialog to select the files
	CFileDialog filedialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load image files
	TCHAR* filestructure = new TCHAR[2048*10];
	filedialog.m_ofn.lpstrFile = filestructure;
	filestructure[0] = '\0';
	filedialog.m_ofn.nMaxFile = 2048*10;
	CString dialogcaption("Add images for clustering...");
	filedialog.m_ofn.lpstrTitle = dialogcaption;
	HRESULT m_hResult = (int)filedialog.DoModal();
	if(FAILED(m_hResult))
		return;

	//Inseart the files into the image list
	POSITION fileposition = filedialog.GetStartPosition();
	while(fileposition!=NULL)
	{
		//Inseart the file name into the list
		CString filepath = filedialog.GetNextPathName(fileposition);
		CT2CA asciistring(filepath);
		string filepatha(asciistring);
		imagelist.push_back(filepatha);
		string::size_type filenamelocation = filepatha.rfind('\\');
		CString filename(&(filepatha.c_str()[filenamelocation+1]));
		imagelistbox->AddString(filename);

		//Inseart the cluster and position into the list
		imagecluster.push_back(0);
		clusterlistbox->AddString(CString("0"));
		_3dpoint startposition = {UINT_MAX, UINT_MAX, UINT_MAX};
		imageposition.push_back(startposition);
	}
	delete filestructure;
	//Set the scrollbar information
	scrollbar->SetScrollRange(0, (int)imagelist.size()-MAXITEM, 1);
	SCROLLINFO scrollinfo;
	scrollinfo.cbSize = sizeof(SCROLLINFO);
	scrollbar->GetScrollInfo(&scrollinfo);
	scrollinfo.nPage = 10;
	scrollbar->SetScrollInfo(&scrollinfo, 1);

	UpdateTotalNumber();

	return;
}

void CImageClustering::OnOK()
{
	return;
}

//Scrollbar function
void CImageClustering::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	//Define the min and max position
	int minPos;
	int maxPos;
	pScrollBar->GetScrollRange(&minPos, &maxPos);
	SCROLLINFO scr_info;
	int curPos = pScrollBar->GetScrollPos();
	switch(nSBCode)
	{
	case SB_TOP:
		curPos = minPos;
		break;
	case SB_BOTTOM:
		curPos = maxPos;
		break;
	case SB_LINEUP:
		if(curPos>minPos)
			curPos --;
		break;
	case SB_LINEDOWN:
		if(curPos<maxPos)
			curPos ++;
		break;
	case SB_PAGEUP:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos>minPos)
				curPos = max(minPos, curPos-(int)scr_info.nPage);
		}
		break;
	case SB_PAGEDOWN:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos<maxPos)
				curPos = min(maxPos, curPos+(int)scr_info.nPage);
		}
		break;
	case SB_THUMBTRACK:
		curPos = nPos;
		break;
	}
	//Set the scrollbar information
	scrollbar->SetScrollPos(curPos);
	CWnd::OnVScroll(nSBCode, nPos, pScrollBar);

	//Change the top index item
	imagelistbox->SetTopIndex(curPos);
	clusterlistbox->SetTopIndex(curPos);
}
void CImageClustering::OnLbnSelchangeImageclusteringImagelist()
{
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);

	//Get the current select item
	currentselection = imagelistbox->GetCurSel();
	if(currentselection<0)		//If nothing selected
		return;
	//Set the selection of cluster list
	clusterlistbox->SetCurSel(currentselection);
	//Open the image
	CString filename;
	imagelistbox->GetWindowTextW(filename);
	((CChildFrame*)parent)->m_wndView.OnImageOpen(CString(imagelist[currentselection].c_str()), filename);
	UpdateTotalNumber();

	//Get the top index, set the cluster list box and scrollbar
	clusterlistbox->SetTopIndex(imagelistbox->GetTopIndex());
	scrollbar->SetScrollPos(imagelistbox->GetTopIndex());
	return;
}

void CImageClustering::OnLbnSelchangeImageclusteringClusterlist()
{
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);

	//Get the current select item
	currentselection = clusterlistbox->GetCurSel();
	if(currentselection<0)		//If nothing selected
		return;
	//Set the selection of cluster list
	imagelistbox->SetCurSel(currentselection);
	//Open the image
	CString filename;
	imagelistbox->GetWindowTextW(filename);
	((CChildFrame*)parent)->m_wndView.OnImageOpen(CString(imagelist[currentselection].c_str()), filename);
	UpdateTotalNumber();

	//Get the top index, set the image list box and scrollbar
	imagelistbox->SetTopIndex(clusterlistbox->GetTopIndex());
	scrollbar->SetScrollPos(clusterlistbox->GetTopIndex());

	return;
}

void CImageClustering::OnBnClickedAddfolder()
{
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);

	// Parameters for a folder view
	LPWSTR lpszDir = new WCHAR[_MAX_PATH];
	LPWSTR buffer = new WCHAR[_MAX_PATH];

	::OleInitialize(NULL);
	BROWSEINFO br_info;
	memset(&br_info, 0, sizeof(br_info));

	// Browse the path for images
	br_info.ulFlags = BIF_USENEWUI;
	br_info.hwndOwner = NULL;
	br_info.lpszTitle = _T("Please Select a Folder for Images");

	LPITEMIDLIST pidl;
	pidl = ::SHBrowseForFolder(&br_info);

	if(pidl != NULL)
	{
		// Create a buffer to store the path and then get the path
		if(::SHGetPathFromIDList(pidl, lpszDir) != 0)
		{
			// Set the string value
		}
		// Free the item ID list
		CoTaskMemFree(pidl);
	}
	else
	{
		::AfxMessageBox(CString("No folder selected"), MB_OK, 0);
		return;
	}
	::OleUninitialize();

	//Construct a list of folders
	vector<string> listoffolders;
	CT2CA asciistring(lpszDir);
	listoffolders.push_back(string(asciistring));

	unsigned int i=0;
	do
	{
		//Construct the searching
		_finddata_t data;
		string fpath = listoffolders[i]+"\\*.*";
		long handle = (long)_findfirst(fpath.c_str(), &data);
		//If something found
		if(handle >= 0)
		{
			do
			{
				if(data.attrib & _A_SUBDIR)	//If it is a folder, push it to listoffolders
				{
					if(strcmp(data.name, ".")!=0 && strcmp(data.name, "..")!=0)
						listoffolders.push_back(listoffolders[i] + "\\" + data.name);
				}
				else	//If it is a file, examine if it is an image or not
				{
					//Check the file extension
					string filename(data.name);
					string fileextension(&(filename.c_str()[filename.size()-4]));
					if(fileextension.compare(string(".bmp"))==0 || fileextension.compare(string(".BMP"))==0
						|| fileextension.compare(string(".tif"))==0 || fileextension.compare(string(".TIF"))==0
						|| fileextension.compare(string(".jpg"))==0 || fileextension.compare(string("JPG"))==0)
					{
						imagelist.push_back(listoffolders[i]+string("\\")+filename);
						imagelistbox->AddString(CString(filename.c_str()));
						clusterlistbox->AddString(CString("0"));
						//Inseart the cluster and position into the list
						imagecluster.push_back(0);
						_3dpoint startposition = {UINT_MAX, UINT_MAX, UINT_MAX};
						imageposition.push_back(startposition);
					}
				}
			}while(_findnext(handle, &data)==0);
		}
		_findclose(handle);
		i++;
	}while(i<listoffolders.size());
	
	//Set the scrollbar information
	scrollbar->SetScrollRange(0, (int)imagelist.size()-MAXITEM, 1);
	SCROLLINFO scrollinfo;
	scrollinfo.cbSize = sizeof(SCROLLINFO);
	scrollbar->GetScrollInfo(&scrollinfo);
	scrollinfo.nPage = 10;
	scrollbar->SetScrollInfo(&scrollinfo, 1);

	UpdateTotalNumber();

	return;
}

//Update the total number of image in the list
void CImageClustering::UpdateTotalNumber(void)
{
	CEdit* totaledit = (CEdit*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGECOUNT);
	//If no image selected
	if(currentselection < 0)
		totaledit->SetWindowTextW(bf.douTostr((unsigned int)imagelist.size()));
	else		//If an image is selected
		totaledit->SetWindowTextW(bf.douTostr(currentselection+1)+(CString("/"))+bf.douTostr((unsigned int)imagelist.size()));
	return;
}

//Start to clustering
void CImageClustering::OnBnClickedImageclustering()
{
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	int loadfeature = ::AfxMessageBox(CString("Press Yes to calculate the features, or No to load features from a file"), MB_YESNO, 0);
	vector<vector<vector<double> > > imagefeatures;
	unsigned int numberofimages = (unsigned int)imagelist.size();
	if(loadfeature == IDYES)
	{
		//Get the image features
		imagecluster.resize(numberofimages);
		imageposition.resize(numberofimages);
		imagefeatures.resize(numberofimages);
		for(unsigned int i=0; i<(unsigned int)numberofimages; i++)
		{
			CString currentimage(bf.douTostr(i+1)+(CString("/")+bf.douTostr(numberofimages)));
			((CChildFrame*)parent)->m_wndView.OnImageOpen(CString(imagelist[i].c_str()), currentimage);
			imagefeatures[i] = ((CChildFrame*)parent)->m_wndView.GetImageWaveletFeature(3, 3, 3);
		}
		//Save the image features or not?
		int savefeature = ::AfxMessageBox(CString("Would you like to save the features?"), MB_YESNO, 0);
		if(savefeature = IDYES)
		{
			CFileDialog filedialog(FALSE, NULL, NULL, NULL, CString("data file (*.dat)|*.dat||"));
			HRESULT hresult = (HRESULT)filedialog.DoModal();
			if(FAILED(hresult))
				return;
			try		//Try the save the data
			{
				CT2CA ascii(filedialog.GetFileName());
				bf.SaveDataAsBinary(imagefeatures, string(ascii));
			}
			catch(string exception)		//If failed
			{
				::AfxMessageBox(CString(exception.c_str()), MB_OK, 0);
				return;
			}
		}
	}
	else
	{
		//Show a dialog to open the file
		CFileDialog filedialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, CString("data file (*.dat)|*.dat||"));
		HRESULT hresult = (HRESULT)filedialog.DoModal();
		if(FAILED(hresult))
			return;
		try		//Load the data
		{
			CT2CA ascii(filedialog.GetFileName());
			imagefeatures = bf.LoadBinaryData(string(ascii));
		}
		catch(string exception)		//If failed, return
		{
			::AfxMessageBox(CString(exception.c_str()), MB_OK, 0);
			return;
		}
		if(numberofimages!=(unsigned int)imagefeatures.size())	//Match the size of the loaded data with the image features
		{
			::AfxMessageBox(CString("Loaded data not matched image set"), MB_OK, 0);
			return;
		}
	}
	unsigned int radiusofsom = 28;

	map<string, unsigned int> folderindex;	//pattern
	vector<unsigned int> sompattern;	//pattern
	sompattern.reserve(imagelist.size());	//pattern
	for(vector<string>::iterator ite = imagelist.begin(); ite!=imagelist.end(); ++ite)	//pattern
	{	//pattern
		string folder((*ite).c_str(), (*ite).find_last_of('\\')+1);	//pattern
		folderindex.insert(pair<string, unsigned int>(folder, static_cast<unsigned int>(folderindex.size())));		//pattern
	}	//pattern
	for(vector<string>::iterator ite = imagelist.begin(); ite!=imagelist.end(); ++ite)	//pattern
	{	//pattern
		for(map<string, unsigned int>::iterator mapite = folderindex.begin(); mapite!=folderindex.end(); ++mapite)	//pattern
		{	//pattern
			if((*ite).find((*mapite).first)!=string::npos)	//pattern
				sompattern.push_back((*mapite).second);	//pattern
		}	//pattern
	}	//pattern
	std::ostringstream systemstring;
	for(map<string, unsigned int>::iterator mapite = folderindex.begin(); mapite!=folderindex.end(); ++mapite)
		systemstring << (*mapite).first << "\t" << (*mapite).second << "\r\n";
	bf.stringToFile(CString(systemstring.str().c_str()), CString("Save the folder patterns as ..."));
	::AfxMessageBox(CString("Save complete"), MB_OK, 0);

	//Create a hierarchical SOM, train it, and test it
	vector<vector<vector<double> > > trainingweights;
	vector<_3dpoint> trainingwinners;
	unsigned int maxcycle = 5;
	vector<double> maxvalue(imagefeatures[0][0].size(), 1.0);	//Max values for initialization
	vector<double> minvalue(imagefeatures[0][0].size(), 0.0);		//Min values for initialization

	try
	{
		MultiscaleNet hierarchicalsom(radiusofsom, radiusofsom, (unsigned int)((double)imagefeatures[0].size()*2), (unsigned int)imagefeatures[0][0].size(), maxcycle,
			maxvalue, minvalue);	//Create the hierarchical SOM
		vector<vector<double> > meanvalue;
		vector<vector<double> > standarddeviation;
		CImageClustering::NormalizeData(imagefeatures, meanvalue, standarddeviation, 2);	//normalize the data
		for(unsigned int i=0; i<maxcycle; i++)
			//hierarchicalsom.Training(imagefeatures, trainingwinners, trainingweights);		//Train the SOM
			hierarchicalsom.Training(imagefeatures, sompattern, trainingwinners, trainingweights);
		for(unsigned int i=0; i<numberofimages; i++)		//Test the SOM using the image features
		{
			vector<vector<double> > testweight;
			hierarchicalsom.GetTestWinnerWeight(imagefeatures[i], imageposition[i], testweight);
			imagecluster[i] = imageposition[i].x*radiusofsom+imageposition[i].y;
		}
		hierarchicalsom.SaveWeightsToFile("c:\\multiscalenet.txt");
		//test the results
		vector<string> testimages = bf.LoadImagesFromAFolder();
		typedef vector<string>::iterator imageiterator;
		std::ostringstream systemstring;
		for(imageiterator i=testimages.begin(); i!=testimages.end(); ++i)
		{
			((CChildFrame*)parent)->m_wndView.OnImageOpen(CString((*i).c_str()), CString("temp"));
			_3dpoint testposition;
			vector<vector<double> > testweight;
			vector<vector<double> > currentfeature = ((CChildFrame*)parent)->m_wndView.GetImageWaveletFeature(3, 3, 3);
			for(unsigned int j=0; j<currentfeature.size(); j++)
			{
				for(unsigned int k=0; k<currentfeature[j].size(); k++)
				{
					currentfeature[j][k] = (currentfeature[j][k]-meanvalue[j][k])/standarddeviation[j][k];
				}
			}
			unsigned int pattern;
			hierarchicalsom.GetTestWinnerWeight(currentfeature, testposition, testweight, pattern);
			systemstring << (*i) << '\t' << testposition.x << '\t' << testposition.y << '\t' << testposition.z << '\t' << pattern << "\r\n";
		}
		bf.stringToFile(CString(systemstring.str().c_str()), CString("Save the testing results to ..."));
	}
	catch(string exceptionmessage)
	{
		::AfxMessageBox(CString(exceptionmessage.c_str()), MB_OK, 0);
		return;
	}
/*
	vector<int> singlesompattern;
	singlesompattern.reserve(sompattern.size());
	for(vector<unsigned int>::iterator i=sompattern.begin(); i!=sompattern.end(); i++)
		singlesompattern.push_back(static_cast<int>(++(*i)));

	//Use SOM to cluster images
	double** inputfeature = new double*[numberofimages];
	unsigned int datadimension;
	for(unsigned int i=0; i<numberofimages; i++)
		inputfeature[i] = bf.VectorToPointer(imagefeatures[i], datadimension);
	double* ave = new double[datadimension];
	double* std = new double[datadimension];
	double** somfeature = bf.matrixTransp(inputfeature, datadimension, numberofimages);
	bf.normalizedData(somfeature, numberofimages, datadimension, ave, std);

	unsigned int maxcycle = 10;
	CSOM som(radiusofsom, radiusofsom, (int)datadimension, (int)1);
	for(unsigned int i=0; i<maxcycle; i++)
		som.somTraining(somfeature, numberofimages, &(singlesompattern[0]));		//pattern
	int* resultpattern = som.somGetLatticePattern();	//pattern
	double* testfeature = new double[datadimension];
	for(unsigned int i=0; i<numberofimages; i++)
	{
		for(unsigned int j=0; j<datadimension; j++)
			testfeature[j] = somfeature[j][i];
		imagecluster[i] = som.getTestingResults(testfeature);
		imageposition[i].x = imagecluster[i]%radiusofsom;
		imageposition[i].y = imagecluster[i]/radiusofsom;
		imageposition[i].z = 0;
	}
	//testing the map
	vector<string> testimages = bf.LoadImagesFromAFolder();
	typedef vector<string>::iterator imageiterator;
	for(imageiterator i=testimages.begin(); i!=testimages.end(); ++i)
	{
		((CChildFrame*)parent)->m_wndView.OnImageOpen(CString((*i).c_str()), CString("temp"));
		vector<vector<double> > testweight;
		vector<vector<double> > currentfeature = ((CChildFrame*)parent)->m_wndView.GetImageWaveletFeature(3, 3, 3);
		unsigned int numberofdata;
		double* featurepointer = bf.VectorToPointer(currentfeature, numberofdata);
		for(unsigned int j=0; j<numberofdata; j++)
				featurepointer[j] = (featurepointer[j]-ave[j])/std[j];
		int testresult = som.getTestingResults(featurepointer);
		systemstring << (*i) << '\t' << testresult%radiusofsom << '\t' << testresult/radiusofsom << '\t' << resultpattern[testresult] << "\r\n";
	}
	bf.stringToFile(CString(systemstring.str().c_str()), CString("Save the testing results to ..."));
*/
	//Output the results
	clusterlistbox->ResetContent();	
	for(unsigned int i=0; i<numberofimages; i++)
		clusterlistbox->AddString(bf.douTostr(imagecluster[i]));

	return;
}

//Normalize the input
void CImageClustering::NormalizeData(std::vector<vector<vector<double> > > &input, unsigned int normalizescale)
{
	//Varify the size of the inputdata
	if(input.size()==0)
		throw string("No data in input");
	else if(input[0].size()==0)
		throw string("No data in first input");

	unsigned int size1 = (unsigned int)input.size();
	unsigned int size2 = (unsigned int)input[0].size();
	unsigned int size3 = (unsigned int)input[0][0].size();

	vector<vector<double> > meanvalue;
	vector<vector<double> > standarddeviation;
	meanvalue.resize(size2);
	standarddeviation.resize(size2);
	for(unsigned int i=0; i<size2; i++)
	{
		meanvalue[i].resize(size3);
		standarddeviation[i].resize(size3);
		for(unsigned int j=0; j<size3; j++)
		{
			meanvalue[i][j] = 0.0;
			standarddeviation[i][j] = 0.0;
		}
	}

	//Loop to get the mean
	for(unsigned int i=0; i<size1; i++)
	{
		if((unsigned int)input[i].size()!=size2)
			throw string("Size of second dimension not equal");
		for(unsigned int j=0; j<size2; j++)
		{
			if((unsigned int)input[i][j].size()!=size3)
				throw string("Size of third dimension not equal");
			for(unsigned int k=0; k<size3; k++)
			{
				meanvalue[j][k] += input[i][j][k];
			}
		}
	}

	for(unsigned int i=0; i<size2; i++)
	{
		for(unsigned int j=0; j<size3; j++)
		{
			meanvalue[i][j] /= (double)size1;
		}
	}

	//Calculate standard deviation
	for(unsigned int i=0; i<size1; i++)
		for(unsigned int j=0; j<size2; j++)
			for(unsigned int k=0; k<size3; k++)
				standarddeviation[j][k] += (input[i][j][k]-meanvalue[j][k])*(input[i][j][k]-meanvalue[j][k]);

	for(unsigned int i=0; i<size2; i++)
	{
		for(unsigned int j=0; j<size3; j++)
		{
			standarddeviation[i][j] = sqrt(standarddeviation[i][j]/((double)size1-1.0));
			if(standarddeviation[i][j] == 0)
				standarddeviation[i][j] = 1;
		}
	}

	//Normalize the data
	for(unsigned int i=0; i<size1; i++)
	{
		for(unsigned int j=0; j<size2; j++)
		{
			for(unsigned int k=0; k<size3; k++)
			{
				input[i][j][k] = (input[i][j][k]-meanvalue[j][k])/standarddeviation[j][k];
			}
		}
	}

	return;
}

//normalize the data and return the mean and standard deviation
void CImageClustering::NormalizeData(std::vector<vector<vector<double> > > &input, std::vector<vector<double> > &meanvalue, std::vector<vector<double> >& standarddeviation, unsigned int normalizescale)
{
	//Varify the size of the inputdata
	if(input.empty())
		throw string("No data in input");
	else if(input[0].empty())
		throw string("No data in first input");

	unsigned int size1 = (unsigned int)input.size();
	unsigned int size2 = (unsigned int)input[0].size();
	unsigned int size3 = (unsigned int)input[0][0].size();

	meanvalue.resize(size2);
	standarddeviation.resize(size2);
	for(unsigned int i=0; i<size2; i++)
	{
		meanvalue[i].resize(size3);
		standarddeviation[i].resize(size3);
		for(unsigned int j=0; j<size3; j++)
		{
			meanvalue[i][j] = 0.0;
			standarddeviation[i][j] = 0.0;
		}
	}

	//Loop to get the mean
	for(unsigned int i=0; i<size1; i++)
	{
		if((unsigned int)input[i].size()!=size2)
			throw string("Size of second dimension not equal");
		for(unsigned int j=0; j<size2; j++)
		{
			if((unsigned int)input[i][j].size()!=size3)
				throw string("Size of third dimension not equal");
			for(unsigned int k=0; k<size3; k++)
			{
				meanvalue[j][k] += input[i][j][k];
			}
		}
	}

	for(unsigned int i=0; i<size2; i++)
	{
		for(unsigned int j=0; j<size3; j++)
		{
			meanvalue[i][j] /= (double)size1;
		}
	}

	//Calculate standard deviation
	for(unsigned int i=0; i<size1; i++)
		for(unsigned int j=0; j<size2; j++)
			for(unsigned int k=0; k<size3; k++)
				standarddeviation[j][k] += (input[i][j][k]-meanvalue[j][k])*(input[i][j][k]-meanvalue[j][k]);

	for(unsigned int i=0; i<size2; i++)
	{
		for(unsigned int j=0; j<size3; j++)
		{
			standarddeviation[i][j] = sqrt(standarddeviation[i][j]/((double)size1-1.0));
			if(standarddeviation[i][j] == 0)
				standarddeviation[i][j] = 1;
		}
	}

	//Normalize the data
	for(unsigned int i=0; i<size1; i++)
	{
		for(unsigned int j=0; j<size2; j++)
		{
			for(unsigned int k=0; k<size3; k++)
			{
				input[i][j][k] = (input[i][j][k]-meanvalue[j][k])/standarddeviation[j][k];
			}
		}
	}

	return;
}


void CImageClustering::OnBnClickedImageclusteringSorbyimage()
{
	unsigned int numberofimages = (unsigned int)imagecluster.size();
	for(unsigned int i=0; i<numberofimages-1; i++)
	{
		for(unsigned int j=i+1; j<numberofimages; j++)
		{
			//If j<i, swap
			if(imagelist[i].compare(imagelist[j])<0)
			{
				//Swap the cluster
				unsigned int tempcluster = imagecluster[i];
				imagecluster[i] = imagecluster[j];
				imagecluster[j] = tempcluster;
				//Swap the position
				_3dpoint tempposition = imageposition[i];
				imageposition[i] = imageposition[j];
				imageposition[j] = tempposition;
				//Swap the filename
				string tempfilepath = imagelist[i];
				imagelist[i] = imagelist[j];
				imagelist[j] = tempfilepath;
			}
		}
	}

	//Clear all the content and reset the new content
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	imagelistbox->ResetContent();
	clusterlistbox->ResetContent();
	for(unsigned int i=0; i<numberofimages; i++)
	{
		//Find the image name
		string::size_type filenameposition = imagelist[i].rfind('\\');
		imagelistbox->AddString(CString(&(imagelist[i].c_str()[(unsigned int)filenameposition+1])));
		clusterlistbox->AddString(bf.douTostr(imagecluster[i]));
	}
	//Set the top to the scroll position
	imagelistbox->SetTopIndex(scrollbar->GetScrollPos());
	clusterlistbox->SetTopIndex(scrollbar->GetScrollPos());

	return;
}

//Sort the list by the clusters
void CImageClustering::OnBnClickedImageclusteringSorbycluster()
{
	unsigned int numberofimages = (unsigned int)imagecluster.size();
	for(unsigned int i=0; i<numberofimages-1; i++)
	{
		for(unsigned int j=i+1; j<numberofimages; j++)
		{
			//If j<i, swap
			if(imagecluster[j]<imagecluster[i])
			{
				//Swap the cluster
				unsigned int tempcluster = imagecluster[i];
				imagecluster[i] = imagecluster[j];
				imagecluster[j] = tempcluster;
				//Swap the position
				_3dpoint tempposition = imageposition[i];
				imageposition[i] = imageposition[j];
				imageposition[j] = tempposition;
				//Swap the filename
				string tempfilepath = imagelist[i];
				imagelist[i] = imagelist[j];
				imagelist[j] = tempfilepath;
			}
		}
	}

	//Clear all the content and reset the new content
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	imagelistbox->ResetContent();
	clusterlistbox->ResetContent();
	for(unsigned int i=0; i<numberofimages; i++)
	{
		//Find the image name
		string::size_type filenameposition = imagelist[i].rfind('\\');
		imagelistbox->AddString(CString(&(imagelist[i].c_str()[(unsigned int)filenameposition+1])));
		clusterlistbox->AddString(bf.douTostr(imagecluster[i]));
	}
	//Set the top to the scroll position
	imagelistbox->SetTopIndex(scrollbar->GetScrollPos());
	clusterlistbox->SetTopIndex(scrollbar->GetScrollPos());

	return;
}

void CImageClustering::OnBnClickedRemovefall()
{
	//Reset the listboxes
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	imagelistbox->ResetContent();
	clusterlistbox->ResetContent();

	//Clean the list
	imagelist.clear();
	imagecluster.clear();
	imageposition.clear();

	UpdateTotalNumber();

	return;
}

void CImageClustering::OnBnClickedImageclusteringSaveresults()
{
	//Varify the save function
	if(imageposition.size()<=0)
	{
		::AfxMessageBox(CString("No image loaded"), MB_OK, 0);
		return;
	}

	//If not trained
	if(imageposition[0].x == UINT_MAX)
	{
		::AfxMessageBox(CString("Haven't trained the image yet"), MB_OK, 0);
		return;
	}
	//Prepare the results into a text file
	std::ostringstream systemstring;
	unsigned int numberofimages = (unsigned int)imageposition.size();
	for(unsigned int i=0; i<numberofimages; i++)
	{
		if(imageposition[i].x == UINT_MAX)
			bf.WriteToLog(CString("Image: ")+(CString(imagelist[i].c_str())+CString(" not trained yet")));
		systemstring << imagelist[i] << '\t' << imageposition[i].x << '\t' << imageposition[i].y << '\t' << imageposition[i].z <<
			'\t' << imagecluster[i] << "\r\n";
	}

	//Save the image into a file
	bf.stringToFile(CString(systemstring.str().c_str()), CString("Save the clustering results as ..."));

	return;
}

void CImageClustering::OnBnClickedImageclusteringLoadresults()
{
	//Clear results
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	clusterlistbox->ResetContent();
	imagelistbox->ResetContent();
	imagelist.clear();
	imagecluster.clear();
	imageposition.clear();
	//Open the text file and load the data
	CFileDialog filedialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, CString("text file (*.txt)|*.txt||"));
	HRESULT hresult = (HRESULT)filedialog.DoModal();
	if(FAILED(hresult))
	{
		::AfxMessageBox(CString("No file selected!"), MB_OK, 0);
		return;
	}
	
	//Convert the filepath into ASCII and open the file
	CT2CA ascii(filedialog.GetFileName());
	string filepath(ascii);
	std::ifstream filestream(filepath.c_str());

	unsigned int counter = 0;
	unsigned int numberofimages = (unsigned int)imageposition.size();
	while(!filestream.eof())
	{
		string eachline;
		std::getline(filestream, eachline);
		if(eachline.size()<=10)		//If too less content in the line, ignore the line
			continue;
		//Image cluster
		unsigned int lasttab = bf.SearchBackward('\t', eachline.c_str(), (unsigned int)eachline.size()-1, (unsigned int)eachline.size());
		if(lasttab==string::npos)
		{
			::AfxMessageBox(CString("File format not correct"), MB_OK, 0);
			return;
		}
		unsigned int cluster = atoi(&(eachline.c_str()[lasttab+1]));
		//Z position
		lasttab = bf.SearchBackward('\t', eachline.c_str(), --lasttab, (unsigned int)eachline.size());
		if(lasttab==string::npos)
		{
			::AfxMessageBox(CString("File format not correct"), MB_OK, 0);
			return;
		}
		unsigned int positionz = atoi(&(eachline.c_str()[lasttab+1]));
		//Y position
		lasttab = bf.SearchBackward('\t', eachline.c_str(), --lasttab, (unsigned int)eachline.size());
		if(lasttab==string::npos)
		{
			::AfxMessageBox(CString("File format not correct"), MB_OK, 0);
			return;
		}
		unsigned int positiony = atoi(&(eachline.c_str()[lasttab+1]));
		//X position
		lasttab = bf.SearchBackward('\t', eachline.c_str(), --lasttab, (unsigned int)eachline.size());
		if(lasttab==string::npos)
		{
			::AfxMessageBox(CString("File format not correct"), MB_OK, 0);
			return;
		}
		unsigned int positionx = atoi(&(eachline.c_str()[lasttab+1]));
		//File path
		string filepath(eachline.c_str(), lasttab);

		//Push the list into the list
		imagelist.push_back(filepath);
		_3dpoint newposition = {positionx, positiony, positionz};
		imageposition.push_back(newposition);
		imagecluster.push_back(cluster);
	}

	//Show the loaded results
	for(unsigned int i=0; i<imagecluster.size(); i++)
	{
		imagelistbox->AddString(CString(imagelist[i].c_str()));
		clusterlistbox->AddString(bf.douTostr(imagecluster[i]));
	}
	//Set the scrollbar information
	scrollbar->SetScrollRange(0, (int)imagelist.size()-MAXITEM, 1);
	SCROLLINFO scrollinfo;
	scrollinfo.cbSize = sizeof(SCROLLINFO);
	scrollbar->GetScrollInfo(&scrollinfo);
	scrollinfo.nPage = 10;
	scrollbar->SetScrollInfo(&scrollinfo, 1);

	return;
}

void CImageClustering::OnBnClickedImageclusteringLoaddb()
{
	CImageClustering::OnBnClickedRemovefall();
	CListBox* imagelistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_IMAGELIST);
	CListBox* clusterlistbox = (CListBox*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_CLUSTERLIST);
	CScrollBar* scrollbar = (CScrollBar*)CImageClustering::GetDlgItem(IDC_IMAGECLUSTERING_SCROLLBAR);
	//Load images list from database
	CImageAnnotation imagedatabase;
	try
	{
		imagedatabase.Open();
	}
	catch(CDBException* e)
	{
		e->ReportError();
		return;
	}
	vector<vector<vector<double>>> imagefeatures;
	while(!imagedatabase.IsEOF())
	{
		string filepath(imagedatabase.m_Image.GetBuffer());
		string filename = string(&(filepath.c_str()[filepath.find_last_of('\\')+1]));
		imagelist.push_back(filepath);
		imagelistbox->AddString(CString(filename.c_str()));
		if(imagedatabase.m_Feature.m_dwDataLength == 0)
			continue;
		CFile cfile(CString("c:\\temp.dat"), CFile::modeCreate|CFile::modeWrite);
		LPSTR buffer = (LPSTR)GlobalLock(imagedatabase.m_Feature.m_hData);
		cfile.Write(buffer, (UINT)imagedatabase.m_Feature.m_dwDataLength);
		cfile.Close();
		vector<vector<double>> currentfeature;
		bf.LoadBinaryData(currentfeature, string("c:\\temp.dat"));
		imagefeatures.push_back(currentfeature);
		imagedatabase.MoveNext();
	}
	unsigned int numberofimages = (unsigned int)imagefeatures.size();
	imagecluster.resize(numberofimages);
	imageposition.resize(numberofimages);
/*
	//Use SOM to cluster images
	double** inputfeature = new double*[numberofimages];
	unsigned int datadimension;
	for(unsigned int i=0; i<numberofimages; i++)
		inputfeature[i] = bf.VectorToPointer(imagefeatures[i], datadimension);
	double* ave = new double[datadimension];
	double* std = new double[datadimension];
	double** somfeature = bf.matrixTransp(inputfeature, numberofimages, datadimension);
	bf.normalizedData(somfeature, numberofimages, datadimension, ave, std);
	unsigned int maxcycle = 2;
	CSOM som(16, 16, (int)datadimension, (int)2);
	for(unsigned int i=0; i<maxcycle; i++)
		som.somTraining(somfeature, numberofimages);
	double* testfeature = new double[datadimension];
	for(unsigned int i=0; i<numberofimages; i++)
	{
		for(unsigned int j=0; j<datadimension; j++)
			testfeature[j] = somfeature[j][i];
		imagecluster[i] = som.getTestingResults(testfeature);
		imageposition[i].x = imagecluster[i]%16;
		imageposition[i].y = imagecluster[i]/16;
		imageposition[i].z = 0;
	}

*/
	//Create a hierarchical SOM, train it, and test it
	vector<vector<vector<double>>> trainingweights;
	vector<_3dpoint> trainingwinners;
	unsigned int maxcycle = 1;
	vector<double> maxvalue;	//Max values for initialization
	vector<double> minvalue;		//MIn values for initialization
	for(unsigned int i=0; i<(unsigned int)imagefeatures[0][0].size(); i++)
	{
		maxvalue.push_back(1.0);
		minvalue.push_back(0.0);
	}
	try
	{
		MultiscaleNet hierarchicalsom(16, 16, (unsigned int)imagefeatures[0].size()*2, (unsigned int)imagefeatures[0][0].size(), maxcycle,
			maxvalue, minvalue);	//Create the hierarchical SOM
		CImageClustering::NormalizeData(imagefeatures, 2);
		for(unsigned int i=0; i<maxcycle; i++)
			hierarchicalsom.Training(imagefeatures, trainingwinners, trainingweights);		//Train the SOM
		for(unsigned int i=0; i<numberofimages; i++)		//Test the SOM using the image features
		{
			vector<vector<double>> testweight;
			hierarchicalsom.GetTestWinnerWeight(imagefeatures[i], imageposition[i], testweight);
			imagecluster[i] = imageposition[i].x*16+imageposition[i].y;
		}
	}
	catch(string exceptionmessage)
	{
		::AfxMessageBox(CString(exceptionmessage.c_str()), MB_OK, 0);
		return;
	}

	//Output the results
	CImageClustering::UpdateTotalNumber();
	clusterlistbox->ResetContent();	
	for(unsigned int i=0; i<numberofimages; i++)
		clusterlistbox->AddString(bf.douTostr(imagecluster[i]));
	//Set the scrollbar information
	scrollbar->SetScrollRange(0, (int)imagelist.size()-MAXITEM, 1);
	SCROLLINFO scrollinfo;
	scrollinfo.cbSize = sizeof(SCROLLINFO);
	scrollbar->GetScrollInfo(&scrollinfo);
	scrollinfo.nPage = 10;
	scrollbar->SetScrollInfo(&scrollinfo, 1);

	return;
}

void CImageClustering::ClusteringExperiment(void)
{
	//open a folder of training images
	vector<string> trainimages = bf.LoadImagesFromAFolder(string("C:\\Documents and Settings\\Administrator\\My Documents\\tcd\\Image classification\\Classification sets\\training"));
	//obtain a testing set
	vector<string> testimages = bf.LoadImagesFromAFolder(string("C:\\Documents and Settings\\Administrator\\My Documents\\tcd\\Image classification\\Classification sets\\testing"));
	//convert the folder into lower case
	typedef vector<string>::iterator imageite;
	typedef string::iterator stringite;
	for(imageite i = trainimages.begin(); i!=trainimages.end(); ++i)
		(*i) = bf.ChangeToLowerCase((*i));
	for(imageite i = testimages.begin(); i!=testimages.end(); ++i)
		(*i) = bf.ChangeToLowerCase((*i));

	//determine the pattern of the folder
	map<string, unsigned int> folderindex;
	vector<unsigned int> sompattern;
	typedef string::size_type stringsize;
	sompattern.reserve(imagelist.size());
	for(imageite ite = trainimages.begin(); ite!=trainimages.end(); ++ite)
	{
		stringsize startposition = (*ite).find_last_of('\\', (*ite).find_last_of('\\')-1)+1;
		stringsize endposition = (*ite).find_last_of('\\')+1;
		string folder(&((*ite).c_str()[startposition]), endposition-startposition);
		folderindex.insert(pair<string, unsigned int>(folder, (static_cast<unsigned int>(folderindex.size())+1)));
	}
	for(imageite ite = trainimages.begin(); ite!=trainimages.end(); ++ite)
	{
		for(map<string, unsigned int>::iterator mapite = folderindex.begin(); mapite!=folderindex.end(); ++mapite)
		{
			if((*ite).find((*mapite).first)!=string::npos)
				sompattern.push_back((*mapite).second);
		}
	}
	//determine the testing folder pattern
	vector<unsigned int> testpattern;
	testpattern.reserve(testimages.size());
	for(imageite ite = testimages.begin(); ite!=testimages.end(); ++ite)
	{
		for(map<string, unsigned int>::iterator mapite = folderindex.begin(); mapite!=folderindex.end(); ++mapite)
		{
			if((*ite).find((*mapite).first)!=string::npos)
				testpattern.push_back((*mapite).second);
		}
	}

	//open images and calculate features
	vector<vector<vector<double> > > imagefeatures;
	imagefeatures.reserve(trainimages.size());
	for(imageite i=trainimages.begin(); i!=trainimages.end(); i++)
	{
		((CChildFrame*)parent)->m_wndView.OnImageOpen(CString((*i).c_str()), CString("temp"));
		imagefeatures.push_back(((CChildFrame*)parent)->m_wndView.GetImageWaveletFeature(3, 3, 3));
	}
	//get the features of the testing images
	vector<vector<vector<double> > > testfeatures;
	testfeatures.reserve(testimages.size());
	for(imageite i = testimages.begin(); i!=testimages.end(); i++)
	{
		((CChildFrame*)parent)->m_wndView.OnImageOpen(CString((*i).c_str()), CString("temp"));
		testfeatures.push_back(((CChildFrame*)parent)->m_wndView.GetImageWaveletFeature(3, 3, 3));
	}


	//normalize the input and output
	//vector<vector<double> > meanvalue;
	//vector<vector<double> > standarddeviation;
	//CImageClustering::NormalizeData(imagefeatures, meanvalue, standarddeviation, 2);

	//for single SOM
	vector<int> singlesompattern;
	singlesompattern.reserve(sompattern.size());
	for(vector<unsigned int>::iterator i = sompattern.begin(); i!=sompattern.end(); ++i)
		singlesompattern.push_back(static_cast<int>(*i));
	vector<int> singletestpattern;
	singletestpattern.reserve(testpattern.size());
	for(vector<unsigned int>::iterator i = testpattern.begin(); i!=testpattern.end(); ++i)
		singletestpattern.push_back(static_cast<int>(*i));
	typedef vector<vector<vector<double> > >::iterator featureite;
	vector<double*> inputfeatures;
	inputfeatures.reserve(imagefeatures.size());
	unsigned int rd1;
	for(featureite k=imagefeatures.begin(); k!=imagefeatures.end(); ++k)
		inputfeatures.push_back(bf.VectorToPointer((*k), rd1));
	vector<double> average(rd1);
	vector<double> stdd(rd1);
	double** trainfeatures = bf.matrixTransp(&(inputfeatures[0]), (int)rd1, (int)inputfeatures.size());
	bf.normalizedData(trainfeatures, (int)inputfeatures.size(), rd1, &(average[0]), &(stdd[0]));

	//create HSOM to clustering the images
	for(unsigned int i=16; i<34; i=i+2)
	{
		const unsigned int rounds = 10;
		vector<double> accuracy;
		accuracy.reserve(rounds);
		for(unsigned int j=1; j<=rounds; j++)
		{
			try
			{
/*				//training the map
				vector<vector<vector<double> >  > trainweights;
				vector<_3dpoint> trainwinners;
				const unsigned int maxcycle = 1;
				vector<double> maxvalue(imagefeatures[0][0].size());
				vector<double> minvalue(imagefeatures[0][0].size());
				for_each(maxvalue.begin(), maxvalue.end(), initializevalue<double>(1.0));
				for_each(minvalue.begin(), minvalue.end(), initializevalue<double>(0.0));
				MultiscaleNet hsom(i, i, (unsigned int)((double)imagefeatures[0].size()*(double)1.5), (unsigned int)imagefeatures[0][0].size(),
					maxcycle, maxvalue, minvalue);
				for(unsigned int k=0; k<maxcycle; k++)
					hsom.Training(imagefeatures, sompattern, trainwinners, trainweights);
				std::ostringstream systemstring;
				vector<unsigned int> result;
				result.reserve(testfeatures.size());
				typedef vector<unsigned int>::iterator patternite;
				imageite testite = testimages.begin();
				patternite pite = testpattern.begin();
				for(vector<vector<vector<double> > >::iterator ite = testfeatures.begin(); ite!=testfeatures.end(); ++ite)
				{
					_3dpoint testposition;
					vector<vector<double> > testweight;
					vector<vector<double> > currentfeature = (*ite);
					for(unsigned int m=0; m<currentfeature.size(); m++)
					{
						for(unsigned int n=0; n<currentfeature[m].size(); n++)
						{
							currentfeature[m][n] = (currentfeature[m][n]-meanvalue[m][n])/standarddeviation[m][n];
						}
					}
					unsigned int pattern;
					hsom.GetTestWinnerWeight(currentfeature, testposition, testweight, pattern);
					systemstring << (*testite) << '\t' << testposition.x << '\t' << testposition.y << '\t' << testposition.z << '\t' << pattern << "\r\n";
					result.push_back(static_cast<unsigned int>((pattern==(*pite))?1:0));
					++pite;
					++testite;
				}
				//build the file name for saving
				CString filename("C:\\Documents and Settings\\Administrator\\My Documents\\tcd\\Image classification\\map");
				filename += bf.douTostr(i);
				filename += (CString("round")+bf.douTostr(j));
				filename += (CString(".txt"));
				bf.WriteToFile(filename, CString(systemstring.str().c_str()));
				accuracy.push_back(static_cast<double>(accumulate(result.begin(), result.end(), 0)));
*/
				//train the single SOM
				vector<double> minvalue(rd1, 0.0);
				vector<double> maxvalue(rd1, 1.0);
				const unsigned int maxcycle = 10;
				CSOM som(i, i, rd1, maxcycle, &(maxvalue[0]), &(minvalue[0]));
				for(unsigned int k=0; k<maxcycle; k++)
					som.somTraining(trainfeatures, (int)inputfeatures.size(), &(singlesompattern[0]));
				//test the single SOM
				imageite imgite = testimages.begin();
				typedef vector<int>::iterator patternite;
				patternite pite = singletestpattern.begin();
				vector<int> result;
				result.reserve(testfeatures.size());
				int* resultpattern = som.somGetLatticePattern();
				std::ostringstream systemstring;
				for(featureite k = testfeatures.begin(); k!=testfeatures.end(); ++k)
				{
					vector<vector<double> > currentfeature = (*k);
					unsigned int td1;
					double* testfeature = bf.VectorToPointer(currentfeature, td1);
					for(int m=0; m<(int)td1; m++)
						testfeature[m] = (testfeature[m]-average[m])/stdd[m];
					int testresult = som.getTestingResults(testfeature);
					systemstring << (*imgite) << '\t' << testresult%i << '\t' << testresult/i << '\t' << resultpattern[testresult] << "\r\n";
					result.push_back(static_cast<unsigned int>(((resultpattern[testresult]+1)==(*pite))?1:0));
					++pite;
					++imgite;
				}
				//build the file name for saving
				CString filename("C:\\Documents and Settings\\Administrator\\My Documents\\tcd\\Image classification\\singlemap");
				filename += bf.douTostr(i);
				filename += (CString("round")+bf.douTostr(j));
				filename += (CString(".txt"));
				bf.WriteToFile(filename, CString(systemstring.str().c_str()));
				accuracy.push_back(static_cast<double>(accumulate(result.begin(), result.end(), 0)));
				som.somDestroy();
			}
			catch(string message)
			{
				::AfxMessageBox(CString(message.c_str()), MB_OK, 0);
				return;
			}
		}
		//output the average accuracy
		CString filename("C:\\Documents and Settings\\Administrator\\My Documents\\tcd\\Image classification\\map");
		filename += bf.douTostr(i);
		filename += (CString("accuracy.txt"));
		bf.WriteToFile(filename, bf.douTostr(static_cast<double>(accumulate(accuracy.begin(), accuracy.end(), 0.0))));
	}
	return;
}
